
#ifndef __DIM_SUM_ELF_H
#define __DIM_SUM_ELF_H

#include <dim-sum/accurate_counter.h>
#include <asm/elf.h>
#include <uapi/dim-sum/elf.h>

#if ELF_EXEC_PAGESIZE > PAGE_SIZE
#define ELF_MIN_ALIGN	ELF_EXEC_PAGESIZE
#else
#define ELF_MIN_ALIGN	PAGE_SIZE
#endif

#define ELF_PAGESTART(_v) ((_v) & ~(unsigned long)(ELF_MIN_ALIGN-1))
#define ELF_PAGEOFFSET(_v) ((_v) & (ELF_MIN_ALIGN-1))
#define ELF_PAGEALIGN(_v) (((_v) + ELF_MIN_ALIGN - 1) & ~(ELF_MIN_ALIGN - 1))

#ifndef elf_read_implies_exec
  /* Executables for which elf_read_implies_exec() returns TRUE will
     have the READ_IMPLIES_EXEC personality flag set automatically.
     Override in asm/elf.h as needed.  */
# define elf_read_implies_exec(ex, have_pt_gnu_stack)	0
#endif
#ifndef SET_PERSONALITY
#define SET_PERSONALITY(ex) \
	set_personality(PER_LINUX | (current->personality & (~PER_MASK)))
#endif

#ifndef SET_PERSONALITY2
#define SET_PERSONALITY2(ex, state) \
	SET_PERSONALITY(ex)
#endif

#if ELF_CLASS == ELFCLASS32

extern Elf32_Dyn _DYNAMIC [];
#define elfhdr		elf32_hdr
#define elf_phdr	elf32_phdr
#define elf_shdr	elf32_shdr
#define elf_note	elf32_note
#define elf_auxv	elf32_auxv
#define elf_addr_t	Elf32_Off
#define Elf_Half	Elf32_Half
#define Elf_Word	Elf32_Word

#else

extern Elf64_Dyn _DYNAMIC [];
#define elfhdr		elf64_hdr
#define elf_phdr	elf64_phdr
#define elf_shdr	elf64_shdr
#define elf_note	elf64_note
#define elf_auxv	elf64_auxv
#define elf_addr_t	Elf64_Off
#define Elf_Half	Elf64_Half
#define Elf_Word	Elf64_Word

#endif

struct elf_seg_info {
	struct accurate_counter conut;
	struct elf_phdr elf_phdrinfo;
};

static inline int file_is_elf(struct elfhdr* elf)
{
	if (elf->e_ident[EI_MAG0] != ELFMAG0 ||
	   elf->e_ident[EI_MAG1] != ELFMAG1 ||
	   elf->e_ident[EI_MAG2] != ELFMAG2 ||
	   elf->e_ident[EI_MAG3] != ELFMAG3) {
		return false;
	}
	return true;
}

static inline int elf_segment_is_interp(struct elf_phdr* phdr)
{
	if (!phdr)
		return false;
	if (phdr->p_type == PT_INTERP)
		return true;
	return false;
}

static inline int elf_segment_is_note(struct elf_phdr* phdr)
{
	if (!phdr)
		return false;
	if (phdr->p_type == PT_NOTE)
		return true;
	return false;
}

static inline int elf_segment_is_dyn(struct elf_phdr* phdr)
{
	if (!phdr)
		return false;
	if (phdr->p_type == PT_DYNAMIC)
		return true;
	return false;
}

static inline int elf_segment_is_load(struct elf_phdr* phdr)
{
	if (!phdr)
		return false;
	if (phdr->p_type == PT_LOAD)
		return true;
	return false;
}

static inline int elf_segment_is_write(struct elf_phdr* phdr)
{
	if (!phdr)
		return false;
	if (phdr->p_flags & PHF_W_MASK)
		return true;
	return false;
}

static inline int elf_segment_is_read(struct elf_phdr* phdr)
{
	if (!phdr)
		return false;
	if (phdr->p_flags & PHF_R_MASK)
		return true;
	return false;
}

static inline int elf_segment_is_exec(struct elf_phdr* phdr)
{
	if (!phdr)
		return false;
	if (phdr->p_flags & PHF_X_MASK)
		return true;
	return false;
}

static inline virt_addr_t elf_segment_virtaddr(struct elf_phdr* phdr)
{
	if (!phdr)
		return (virt_addr_t)NULL;
	return (virt_addr_t)(phdr->p_vaddr);
}

static inline virt_addr_t elf_segment_virtend(struct elf_phdr* phdr)
{
	if (!phdr)
		return (virt_addr_t)NULL;
	if (!phdr->p_vaddr)
		return (virt_addr_t)NULL;
	return (virt_addr_t)(phdr->p_vaddr + phdr->p_memsz);
}

static inline phys_addr_t elf_segment_physaddr(struct elf_phdr* phdr)
{
	if (!phdr)
		return (phys_addr_t)NULL;
	return (phys_addr_t)(phdr->p_paddr);
}

static inline phys_addr_t elf_segment_physend(struct elf_phdr* phdr)
{
	if (!phdr)
		return (phys_addr_t)NULL;
	if (!phdr->p_paddr)
		return (phys_addr_t)NULL;
	return (phys_addr_t)(phdr->p_paddr + phdr->p_memsz);
}

static inline virt_size_t elf_segment_fsize(struct elf_phdr* phdr)
{
	if (!phdr)
		return 0;
	return (virt_size_t)(phdr->p_filesz);
}

static inline virt_size_t elf_segment_msize(struct elf_phdr* phdr)
{
	if (!phdr)
		return 0;
	return (virt_size_t)(phdr->p_memsz);
}

static inline virt_addr_t elf_segment_in_file_addr(struct elf_phdr* phdr)
{
	if (!phdr)
		return (virt_addr_t)NULL;
	return (virt_addr_t)(phdr->p_offset);
}

static inline virt_addr_t elf_segment_in_mem_addr(struct elfhdr* elf, struct elf_phdr* phdr)
{
	if (!elf || !phdr)
		return (virt_addr_t)NULL;
	return (virt_addr_t)((elf_addr_t)elf + (elf_addr_t)phdr->p_offset);
}

static inline int elf_section_is_write(struct elf_shdr* shdr)
{
	if (!shdr)
		return false;
	if (shdr->sh_flags & SHF_WRITE_MASK)
		return true;
	return false;
}

static inline int elf_section_is_alloc(struct elf_shdr* shdr)
{
	if (!shdr)
		return false;
	if (shdr->sh_flags & SHF_ALLOC_MASK)
		return true;
	return false;
}

static inline int elf_section_is_exec(struct elf_shdr* shdr)
{
	if (!shdr)
		return false;
	if (shdr->sh_flags & SHF_EXEC_MASK)
		return true;
	return false;
}

static inline virt_addr_t elf_section_virtaddr(struct elf_shdr* shdr)
{
	if (!shdr)
		return (virt_addr_t)NULL;
	return (virt_addr_t)(shdr->sh_addr);
}

static inline virt_addr_t elf_section_virtend(struct elf_shdr* shdr)
{
	if (!shdr)
		return (virt_addr_t)NULL;
	if (!shdr->sh_addr)
		return (virt_addr_t)NULL;
	return (virt_addr_t)(shdr->sh_addr + shdr->sh_size);
}

static inline virt_size_t elf_section_size(struct elf_shdr* shdr)
{
	if (!shdr)
		return 0;
	return (virt_size_t)(shdr->sh_size);
}

static inline virt_addr_t elf_section_in_file_addr(struct elf_shdr* shdr)
{
	if (!shdr)
		return (virt_addr_t)NULL;
	return (virt_addr_t)(shdr->sh_offset);
}

static inline virt_addr_t elf_section_in_mem_addr(struct elfhdr* elf, struct elf_shdr* shdr)
{
	if (!elf || !shdr)
		return (virt_addr_t)NULL;
	return (virt_addr_t)((elf_addr_t)elf + (elf_addr_t)shdr->sh_offset);
}

static inline int elf_is_arch(struct elfhdr* elf)
{
	if (!elf)
		return false;
	if(elf_check_arch(elf))
		return true;
	return false;
}

static inline int elf_is_64bit(struct elfhdr* elf)
{
	if (!elf)
		return false;
	if (elf->e_ident[EI_CLASS] == ELFCLASS64)
		return true;
	return false;
}

static inline int elf_is_exec(struct elfhdr* elf)
{
	if (!elf)
		return false;
	if (elf->e_type == ET_EXEC)
		return true;
	return false;
}

static inline int elf_is_core(struct elfhdr* elf)
{
	if (!elf)
		return false;
	if (elf->e_type == ET_CORE)
		return true;
	return false;
}

static inline int elf_is_dyn(struct elfhdr* elf)
{
	if (!elf)
		return false;
	if (elf->e_type == ET_DYN)
		return true;
	return false;
}
struct elf_seg_info* alloced_elf_seg_info(void);
void free_elf_seg_info(struct elf_seg_info* esi);
struct elf_phdr* elf_phdr_start(struct elfhdr* elf);
struct elf_phdr* elf_phdr_addr(struct elfhdr* elf, int indx);
struct elf_phdr* elf_shdr_start(struct elfhdr* elf);
struct elf_shdr* elf_shdr_addr(struct elfhdr* elf, int indx);
char* elf_get_shstrtab_string(struct elfhdr* elf, int nmindx);
void dump_elf_shdr(struct elfhdr* elf, struct elf_shdr* shdr, char* strseg);
void dump_elf_phdr(struct elfhdr* elf, struct elf_phdr* phdr);
struct elf_shdr* find_section_for_name(struct elfhdr* elf, const char* name);
struct elf_shdr* find_section_for_flags(struct elfhdr* elf, int flags);
int load_elf(void* addr);
struct elf_phdr* elf_load_phtbl(int fd, struct elfhdr* elf);
void elf_free_phtbl(struct elf_phdr* phdr);
struct elf_shdr* elf_load_shtbl(int fd, struct elfhdr* elf);
void elf_free_shtbl(struct elf_shdr* shdr);
char* elf_load_strsegment(int fd, struct elfhdr* elf, struct elf_shdr* tbl);
void elf_free_strsegment(char* segment);
struct elfhdr* elf_load_elfhdr(int fd);
void elf_free_elfhdr(struct elfhdr* elf);
struct exec_info;
int elf_file_preparse(struct exec_info* info, int fd);
int elf_file_execve(struct exec_info* info, int fd, 
						char * const *argv, char * const *envp);
int elf_load_shlib(char* __user lib);

int elf_filemap_fault(struct vma_desc* v, virt_addr_t faddr, 
						loff_t* out_pos, size_t* out_size);
int elf_filenoalign_amend(struct vma_desc* v, virt_addr_t faddr);

#endif /* __DIM_SUM_ELF_H */
